home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / commands / vacuum.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  19.9 KB  |  792 lines

  1. /*
  2.  *  vacuum.c -- the postgres vacuum cleaner
  3.  */
  4.  
  5. #include <sys/file.h>
  6.  
  7. #include "tmp/postgres.h"
  8. #include "tmp/c.h"
  9. #include "tmp/portal.h"
  10.  
  11. #include "access/genam.h"
  12. #include "access/heapam.h"
  13. #include "access/tqual.h"
  14. #include "access/htup.h"
  15.  
  16. #include "catalog/pg_index.h"
  17. #include "catalog/catname.h"
  18. #include "catalog/pg_relation.h"
  19. #include "catalog/pg_proc.h"
  20.  
  21. #include "storage/itemid.h"
  22. #include "storage/bufmgr.h"
  23. #include "storage/bufpage.h"
  24. #include "storage/smgr.h"
  25.  
  26. #include "utils/log.h"
  27. #include "utils/mcxt.h"
  28.  
  29. #include "commands/vacuum.h"
  30.  
  31. RcsId("$Header: /private/postgres/src/commands/RCS/vacuum.c,v 1.11 1992/08/18 18:27:16 mer Exp $");
  32.  
  33. bool    VacuumRunning =    false;
  34.  
  35. vacuum()
  36. {
  37.     /* initialize vacuum cleaner */
  38.     _vc_init();
  39.  
  40.     /* vacuum the database */
  41.     _vc_vacuum();
  42.  
  43.     /* clean up */
  44.     _vc_shutdown();
  45. }
  46.  
  47. /*
  48.  *  _vc_init(), _vc_shutdown() -- start up and shut down the vacuum cleaner.
  49.  *
  50.  *    We run exactly one vacuum cleaner at a time.  We use the file system
  51.  *    to guarantee an exclusive lock on vacuuming, since a single vacuum
  52.  *    cleaner instantiation crosses transaction boundaries, and we'd lose
  53.  *    postgres-style locks at the end of every transaction.
  54.  *
  55.  *    The strangeness with committing and starting transactions in the
  56.  *    init and shutdown routines is due to the fact that the vacuum cleaner
  57.  *    is invoked via a postquel command, and so is already executing inside
  58.  *    a transaction.  We need to leave ourselves in a predictable state
  59.  *    on entry and exit to the vacuum cleaner.  We commit the transaction
  60.  *    started in PostgresMain() inside _vc_init(), and start one in
  61.  *    _vc_shutdown() to match the commit waiting for us back in
  62.  *    PostgresMain().
  63.  */
  64.  
  65. void
  66. _vc_init()
  67. {
  68.     int fd;
  69.  
  70.     if ((fd = open("pg_vlock", O_CREAT|O_EXCL, 0600)) < 0)
  71.     elog(WARN, "can't create lock file -- another vacuum cleaner running?");
  72.  
  73.     close(fd);
  74.  
  75.     /*
  76.      *  By here, exclusive open on the lock file succeeded.  If we abort
  77.      *  for any reason during vacuuming, we need to remove the lock file.
  78.      *  This global variable is checked in the transaction manager on xact
  79.      *  abort, and the routine vc_abort() is called if necessary.
  80.      */
  81.  
  82.     VacuumRunning = true;
  83.  
  84.     /* matches the StartTransaction in PostgresMain() */
  85.     CommitTransactionCommand();
  86. }
  87.  
  88. void
  89. _vc_shutdown()
  90. {
  91.     /* on entry, not in a transaction */
  92.     if (unlink("pg_vlock") < 0)
  93.     elog(WARN, "vacuum: can't destroy lock file!");
  94.  
  95.     /* okay, we're done */
  96.     VacuumRunning = false;
  97.  
  98.     /* matches the CommitTransaction in PostgresMain() */
  99.     StartTransactionCommand();
  100. }
  101.  
  102. void
  103. vc_abort()
  104. {
  105.     /* on abort, remove the vacuum cleaner lock file */
  106.     (void) unlink("pg_vlock");
  107.  
  108.     VacuumRunning = false;
  109. }
  110.  
  111. /*
  112.  *  _vc_vacuum() -- vacuum the database.
  113.  *
  114.  *    This routine builds a list of relations to vacuum, and then calls
  115.  *    code that vacuums them one at a time.  We are careful to vacuum each
  116.  *    relation in a separate transaction in order to avoid holding too many
  117.  *    locks at one time.
  118.  */
  119.  
  120. void
  121. _vc_vacuum()
  122. {
  123.     VRelList vrl, cur;
  124.     char *pname;
  125.     Portal p;
  126.  
  127.     /*
  128.      *  Create a portal for safe memory across transctions.  We need to
  129.      *  palloc the name space for it because our hash function expects
  130.      *    the name to be on a longword boundary.  CreatePortal copies the
  131.      *  name to safe storage for us.
  132.      */
  133.  
  134.     pname = (char *) palloc(strlen(VACPNAME) + 1);
  135.     strcpy(pname, VACPNAME);
  136.     p = CreatePortal(pname);
  137.     pfree(pname);
  138.  
  139.     /* get list of relations */
  140.     vrl = _vc_getrels(p);
  141.  
  142.     /* vacuum each heap relation */
  143.     for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
  144.     _vc_vacone(p, cur);
  145.  
  146.     _vc_free(p, vrl);
  147.  
  148.     PortalDestroy(p);
  149. }
  150.  
  151. VRelList
  152. _vc_getrels(p)
  153.     Portal p;
  154. {
  155.     Relation pgclass;
  156.     TupleDescriptor pgcdesc;
  157.     HeapScanDesc pgcscan;
  158.     HeapTuple pgctup;
  159.     Buffer buf;
  160.     PortalVariableMemory portalmem;
  161.     MemoryContext old;
  162.     VRelList vrl, cur;
  163.     Datum d;
  164.     Name rname;
  165.     int16 smgrno;
  166.     Boolean n;
  167.     ScanKeyEntryData pgckey[1];
  168.  
  169.     StartTransactionCommand();
  170.  
  171.     ScanKeyEntryInitialize(&pgckey[0], 0x0, Anum_pg_relation_relkind,
  172.                CharacterEqualRegProcedure, CharGetDatum('r'));
  173.  
  174.     portalmem = PortalGetVariableMemory(p);
  175.     vrl = (VRelList) NULL;
  176.  
  177.     pgclass = heap_openr(Name_pg_relation);
  178.     pgcdesc = RelationGetTupleDescriptor(pgclass);
  179.     pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey[0]);
  180.  
  181.     while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, false, &buf))) {
  182.  
  183.     /*
  184.      *  We have to be careful not to vacuum the archive (since it
  185.      *  already contains vacuumed tuples), and not to vacuum
  186.      *  relations on write-once storage managers like the Sony
  187.      *  jukebox at Berkeley.
  188.      */
  189.  
  190.     d = (Datum) heap_getattr(pgctup, buf, Anum_pg_relation_relname,
  191.                  pgcdesc, &n);
  192.     rname = DatumGetName(d);
  193.  
  194.     /* skip archive relations */
  195.     if (_vc_isarchrel(rname)) {
  196.         ReleaseBuffer(buf);
  197.         continue;
  198.     }
  199.  
  200.     d = (Datum) heap_getattr(pgctup, buf, Anum_pg_relation_relsmgr,
  201.                  pgcdesc, &n);
  202.     smgrno = DatumGetInt16(d);
  203.  
  204.     /* skip write-once storage managers */
  205.     if (smgriswo(smgrno)) {
  206.         ReleaseBuffer(buf);
  207.         continue;
  208.     }
  209.  
  210.     /* get a relation list entry for this guy */
  211.     old = MemoryContextSwitchTo((MemoryContext)portalmem);
  212.     if (vrl == (VRelList) NULL) {
  213.         vrl = cur = (VRelList) palloc(sizeof(VRelListData));
  214.     } else {
  215.         cur->vrl_next = (VRelList) palloc(sizeof(VRelListData));
  216.         cur = cur->vrl_next;
  217.     }
  218.     (void) MemoryContextSwitchTo(old);
  219.  
  220.     cur->vrl_relid = pgctup->t_oid;
  221.     cur->vrl_attlist = (VAttList) NULL;
  222.     cur->vrl_tidlist = (VTidList) NULL;
  223.     cur->vrl_npages = cur->vrl_ntups = 0;
  224.     cur->vrl_hasindex = false;
  225.     cur->vrl_next = (VRelList) NULL;
  226.  
  227.     /* wei hates it if you forget to do this */
  228.     ReleaseBuffer(buf);
  229.     }
  230.  
  231.     heap_close(pgclass);
  232.     heap_endscan(pgcscan);
  233.  
  234.     CommitTransactionCommand();
  235.  
  236.     return (vrl);
  237. }
  238.  
  239. /*
  240.  *  _vc_vacone() -- vacuum one heap relation
  241.  *
  242.  *    This routine vacuums a single heap, cleans out its indices, and
  243.  *    updates its statistics npages and ntuples statistics.
  244.  *
  245.  *    Doing one heap at a time incurs extra overhead, since we need to
  246.  *    check that the heap exists again just before we vacuum it.  The
  247.  *    reason that we do this is so that vacuuming can be spread across
  248.  *    many small transactions.  Otherwise, two-phase locking would require
  249.  *    us to lock the entire database during one pass of the vacuum cleaner.
  250.  */
  251.  
  252. void
  253. _vc_vacone(p, curvrl)
  254.     Portal p;
  255.     VRelList curvrl;
  256. {
  257.     Relation pgclass;
  258.     TupleDescriptor pgcdesc;
  259.     HeapTuple pgctup;
  260.     Form_pg_relation pgcform;
  261.     Buffer pgcbuf;
  262.     HeapScanDesc pgcscan;
  263.     Relation onerel;
  264.     TupleDescriptor onedesc;
  265.     HeapTuple onetup;
  266.     ScanKeyEntryData pgckey[1];
  267.  
  268.     StartTransactionCommand();
  269.  
  270.     ScanKeyEntryInitialize(&pgckey[0], 0x0, ObjectIdAttributeNumber,
  271.                ObjectIdEqualRegProcedure,
  272.                ObjectIdGetDatum(curvrl->vrl_relid));
  273.  
  274.     pgclass = heap_openr(Name_pg_relation);
  275.     pgcdesc = RelationGetTupleDescriptor(pgclass);
  276.     pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey[0]);
  277.  
  278.     /*
  279.      *  Race condition -- if the pg_class tuple has gone away since the
  280.      *  last time we saw it, we don't need to vacuum it.
  281.      */
  282.  
  283.     if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, false, &pgcbuf))) {
  284.     heap_endscan(pgcscan);
  285.     heap_close(pgclass);
  286.     CommitTransactionCommand();
  287.     return;
  288.     }
  289.  
  290.     /* now open the class and vacuum it */
  291.     onerel = heap_open(curvrl->vrl_relid);
  292.  
  293.     /* we require the relation to be locked until the indices are cleaned */
  294.     RelationSetLockForWrite(onerel);
  295.  
  296.     /* vacuum it */
  297.     _vc_vacheap(p, curvrl, onerel);
  298.  
  299.     /* if we vacuumed any heap tuples, vacuum the indices too */
  300.     if (curvrl->vrl_tidlist != (VTidList) NULL)
  301.     _vc_vacindices(curvrl, onerel);
  302.     else
  303.     curvrl->vrl_hasindex = onerel->rd_rel->relhasindex;
  304.  
  305.     /* all done with this class */
  306.     heap_close(onerel);
  307.     heap_endscan(pgcscan);
  308.     heap_close(pgclass);
  309.  
  310.     /* update statistics in pg_class */
  311.     _vc_updstats(curvrl->vrl_relid, curvrl->vrl_npages, curvrl->vrl_ntups,
  312.          curvrl->vrl_hasindex);
  313.  
  314.     CommitTransactionCommand();
  315. }
  316.  
  317. /*
  318.  *  _vc_vacheap() -- vacuum an open heap relation
  319.  *
  320.  *    This routine sets commit times, vacuums dead tuples, cleans up
  321.  *    wasted space on the page, and maintains statistics on the number
  322.  *    of live tuples in a heap.  In addition, it records the tids of
  323.  *    all tuples removed from the heap for any reason.  These tids are
  324.  *    used in a scan of indices on the relation to get rid of dead
  325.  *    index tuples.
  326.  */
  327.  
  328. void
  329. _vc_vacheap(p, curvrl, onerel)
  330.     Portal p;
  331.     VRelList curvrl;
  332.     Relation onerel;
  333. {
  334.     int nblocks, blkno;
  335.     ItemId itemid;
  336.     HeapTuple htup;
  337.     Buffer buf;
  338.     Page page;
  339.     OffsetIndex offind, maxoff;
  340.     Relation archrel;
  341.     bool isarchived;
  342.     int ntups;
  343.     bool pgchanged, tupgone;
  344.  
  345.     ntups = 0;
  346.     nblocks = RelationGetNumberOfBlocks(onerel);
  347.  
  348.     /* if the relation has an archive, open it */
  349.     if (onerel->rd_rel->relarch != 'n') {
  350.     isarchived = true;
  351.     archrel = _vc_getarchrel(onerel);
  352.     } else
  353.     isarchived = false;
  354.  
  355.     for (blkno = 0; blkno < nblocks; blkno++) {
  356.     buf = ReadBuffer(onerel, blkno);
  357.     page = BufferGetPage(buf, 0);
  358.  
  359.     if (PageIsEmpty(page)) {
  360.         ReleaseBuffer(buf);
  361.         continue;
  362.     }
  363.  
  364.     pgchanged = false;
  365.     maxoff = PageGetMaxOffsetIndex(page);
  366.     for (offind = 0; offind <= maxoff; offind++) {
  367.         itemid = PageGetItemId(page, offind);
  368.  
  369.         if (!ItemIdIsUsed(itemid))
  370.         continue;
  371.  
  372.         htup = (HeapTuple) PageGetItem(page, itemid);
  373.         tupgone = false;
  374.  
  375.         if (AbsoluteTimeIsValid(htup->t_tmin) && 
  376.         TransactionIdIsValid((TransactionId)htup->t_xmin)) {
  377.  
  378.         if (TransactionIdDidAbort(htup->t_xmin)) {
  379.             _vc_reaptid(p, curvrl, blkno, offind);
  380.             pgchanged = true;
  381.             tupgone = true;
  382.         } else if (TransactionIdDidCommit(htup->t_xmin)) {
  383.             htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin);
  384.             pgchanged = true;
  385.         }
  386.         }
  387.  
  388.         if (TransactionIdIsValid((TransactionId)htup->t_xmax)) {
  389.         if (TransactionIdDidAbort(htup->t_xmax)) {
  390.             PointerStoreInvalidTransactionId((Pointer)&(htup->t_xmax));
  391.             pgchanged = true;
  392.         } else if (TransactionIdDidCommit(htup->t_xmax)) {
  393.             if (!AbsoluteTimeIsReal(htup->t_tmax)) {
  394.  
  395.             htup->t_tmax = TransactionIdGetCommitTime(htup->t_xmax);
  396.             pgchanged = true;
  397.             }
  398.  
  399.             /*
  400.              *  Reap the dead tuple.  We should check the commit time
  401.              *  of the deleting transaction against the relation's
  402.              *  expiration time, but relexpires is not maintained by
  403.              *  any postgres code I can find.
  404.              */
  405.  
  406.             if (!tupgone) {
  407.             _vc_reaptid(p, curvrl, blkno, offind);
  408.             tupgone = true;
  409.             pgchanged = true;
  410.             }
  411.         }
  412.         }
  413.  
  414.         if (tupgone) {
  415.  
  416.         /* write the tuple to the archive, if necessary */
  417.         if (isarchived)
  418.             _vc_archive(archrel, htup);
  419.  
  420.         /* mark it unused */
  421.         (((PageHeader) page)->pd_linp[offind]).lp_flags &= ~LP_USED;
  422.         } else {
  423.         ntups++;
  424.         }
  425.     }
  426.  
  427.     if (pgchanged) {
  428.         PageRepairFragmentation(page);
  429.         WriteBuffer(buf);
  430.     } else {
  431.         ReleaseBuffer(buf);
  432.     }
  433.     }
  434.  
  435.     if (isarchived)
  436.     heap_close(archrel);
  437.  
  438.     /* save stats in the rel list for use later */
  439.     curvrl->vrl_ntups = ntups;
  440.     curvrl->vrl_npages = nblocks;
  441. }
  442.  
  443. /*
  444.  *  _vc_vacindices() -- vacuum all the indices for a particular heap relation.
  445.  *
  446.  *    On entry, curvrl points at the relation currently being vacuumed.
  447.  *    We already have a write lock on the relation, so we don't need to
  448.  *    worry about anyone building an index on it while we're doing the
  449.  *    vacuuming.  The tid list for curvrl is sorted in reverse tid order:
  450.  *    that is, tids on higher page numbers are before those on lower page
  451.  *    numbers, and tids high on the page are before those low on the page.
  452.  *    We use this ordering to cut down the search cost when we look at an
  453.  *    index entry.
  454.  *
  455.  *    We're executing inside the transaction that vacuumed the heap.
  456.  */
  457.  
  458. void
  459. _vc_vacindices(curvrl, onerel)
  460.     VRelList curvrl;
  461.     Relation onerel;
  462. {
  463.     Relation pgindex;
  464.     TupleDescriptor pgidesc;
  465.     HeapTuple pgitup;
  466.     HeapScanDesc pgiscan;
  467.     Buffer buf;
  468.     Relation indrel;
  469.     ObjectId indoid;
  470.     Datum d;
  471.     Boolean n;
  472.     int nindices;
  473.     ScanKeyEntryData pgikey[1];
  474.  
  475.     /* see if we can dodge doing any work at all */
  476.     if (!(onerel->rd_rel->relhasindex))
  477.     return;
  478.  
  479.     nindices = 0;
  480.  
  481.     /* prepare a heap scan on the pg_index relation */
  482.     pgindex = heap_openr(Name_pg_index);
  483.     pgidesc = RelationGetTupleDescriptor(pgindex);
  484.  
  485.     ScanKeyEntryInitialize(&pgikey[0], 0x0, Anum_pg_index_indrelid,
  486.                ObjectIdEqualRegProcedure,
  487.                ObjectIdGetDatum(curvrl->vrl_relid));
  488.  
  489.     pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey[0]);
  490.  
  491.     /* vacuum all the indices */
  492.     while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, false, &buf))) {
  493.     d = (Datum) heap_getattr(pgitup, buf, Anum_pg_index_indexrelid,
  494.                  pgidesc, &n);
  495.     indoid = DatumGetObjectId(d);
  496.     indrel = index_open(indoid);
  497.     _vc_vaconeind(curvrl, indrel);
  498.     heap_close(indrel);
  499.     nindices++;
  500.     }
  501.  
  502.     heap_endscan(pgiscan);
  503.     heap_close(pgindex);
  504.  
  505.     if (nindices > 0)
  506.     curvrl->vrl_hasindex = true;
  507.     else
  508.     curvrl->vrl_hasindex = false;
  509. }
  510.  
  511. /*
  512.  *  _vc_vaconeind() -- vacuum one index relation.
  513.  *
  514.  *    Curvrl is the VRelList entry for the heap we're currently vacuuming.
  515.  *    It's locked.  The vrl_tidlist entry in curvrl is the list of deleted
  516.  *    heap tids, sorted in reverse (page, offset) order.  Onerel is an
  517.  *    index relation on the vacuumed heap.  We don't set locks on the index
  518.  *    relation here, since the indexed access methods support locking at
  519.  *    different granularities.  We let them handle it.
  520.  *
  521.  *    Finally, we arrange to update the index relation's statistics in
  522.  *    pg_class.
  523.  */
  524.  
  525. void
  526. _vc_vaconeind(curvrl, indrel)
  527.     VRelList curvrl;
  528.     Relation indrel;
  529. {
  530.     RetrieveIndexResult res;
  531.     IndexScanDesc iscan;
  532.     ItemPointer heapptr;
  533.     int nitups;
  534.     int nipages;
  535.  
  536.     /* walk through the entire index */
  537.     iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
  538.     nitups = 0;
  539.  
  540.     while ((res = index_getnext(iscan, ForwardScanDirection))
  541.        != (RetrieveIndexResult) NULL) {
  542.     heapptr = RetrieveIndexResultGetHeapItemPointer(res);
  543.  
  544.     if (_vc_ontidlist(heapptr, curvrl->vrl_tidlist))
  545.         index_delete(indrel, RetrieveIndexResultGetIndexItemPointer(res));
  546.     else
  547.         nitups++;
  548.  
  549.     /* be tidy */
  550.     pfree(res);
  551.     }
  552.  
  553.     index_endscan(iscan);
  554.  
  555.     /* now update statistics in pg_class */
  556.     nipages = RelationGetNumberOfBlocks(indrel);
  557.     _vc_updstats(indrel->rd_id, nipages, nitups, false);
  558. }
  559.  
  560. /*
  561.  *  _vc_updstats() -- update pg_class statistics for one relation
  562.  *
  563.  *    This routine works for both index and heap relation entries in
  564.  *    pg_class.  We violate no-overwrite semantics here by storing new
  565.  *    values for ntuples, npages, and hasindex directly in the pg_class
  566.  *    tuple that's already on the page.  The reason for this is that if
  567.  *    we updated these tuples in the usual way, then every tuple in pg_class
  568.  *    would be replaced every day.  This would make planning and executing
  569.  *    historical queries very expensive.
  570.  */
  571.  
  572. void
  573. _vc_updstats(relid, npages, ntuples, hasindex)
  574.     ObjectId relid;
  575.     int npages;
  576.     int ntuples;
  577.     bool hasindex;
  578. {
  579.     Relation pgclass;
  580.     HeapScanDesc pgcscan;
  581.     HeapTuple pgctup;
  582.     Buffer buf;
  583.     Form_pg_relation pgcform;
  584.     ScanKeyEntryData pgckey[1];
  585.  
  586.     ScanKeyEntryInitialize(&pgckey[0], 0x0, ObjectIdAttributeNumber,
  587.                ObjectIdEqualRegProcedure,
  588.                ObjectIdGetDatum(relid));
  589.  
  590.     pgclass = heap_openr(Name_pg_relation);
  591.     pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey[0]);
  592.  
  593.     if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, false, &buf)))
  594.     elog(WARN, "pg_class entry for relid %d vanished during vacuuming",
  595.            relid);
  596.  
  597.     /* overwrite the existing statistics in the tuple */
  598.     _vc_setpagelock(pgclass, BufferGetBlockNumber(buf));
  599.     pgcform = (Form_pg_relation) GETSTRUCT(pgctup);
  600.     pgcform->reltuples = ntuples;
  601.     pgcform->relpages = npages;
  602.     pgcform->relhasindex = hasindex;
  603.     pgcform->relpreserved = GetCurrentTransactionStartTime();
  604.  
  605.     /* XXX -- after write, should invalidate relcache in other backends */
  606.     WriteNoReleaseBuffer(buf);
  607.  
  608.     /* that's all, folks */
  609.     heap_endscan(pgcscan);
  610.     heap_close(pgclass);
  611. }
  612.  
  613. void
  614. _vc_setpagelock(rel, blkno)
  615.     Relation rel;
  616.     BlockNumber blkno;
  617. {
  618.     ItemPointerData itm;
  619.  
  620.     ItemPointerSet(&itm, 0, blkno, 0, 1);
  621.  
  622.     RelationSetLockForWritePage(rel, 0, &itm);
  623. }
  624.  
  625. /*
  626.  *  _vc_ontidlist() -- is a particular tid on the supplied tid list?
  627.  *
  628.  *    Tidlist is sorted in reverse (page, offset) order.
  629.  */
  630.  
  631. bool
  632. _vc_ontidlist(itemptr, tidlist)
  633.     ItemPointer itemptr;
  634.     VTidList tidlist;
  635. {
  636.     BlockNumber ibkno;
  637.     OffsetNumber ioffno;
  638.     ItemPointer check;
  639.     BlockNumber ckbkno;
  640.     OffsetNumber ckoffno;
  641.  
  642.     ibkno = ItemPointerGetBlockNumber(itemptr);
  643.     ioffno = ItemPointerGetOffsetNumber(itemptr, 0);
  644.  
  645.     while (tidlist != (VTidList) NULL) {
  646.     check = &(tidlist->vtl_tid);
  647.     ckbkno = ItemPointerGetBlockNumber(check);
  648.     ckoffno = ItemPointerGetOffsetNumber(check, 0);
  649.  
  650.     /* see if we've looked far enough down the list */
  651.     if ((ckbkno < ibkno) || (ckbkno == ibkno && ckoffno < ioffno))
  652.         return (false);
  653.  
  654.     /* see if we have a match */
  655.     if (ckbkno == ibkno && ckoffno == ioffno)
  656.         return (true);
  657.  
  658.     /* check next */
  659.     tidlist = tidlist->vtl_next;
  660.     }
  661.  
  662.     /* ran off the end of the list without finding a match */
  663.     return (false);
  664. }
  665.  
  666. /*
  667.  *  _vc_reaptid() -- save a tid on the list of reaped tids for the current
  668.  *             entry on the vacuum relation list.
  669.  *
  670.  *    As a side effect of the way that the vacuuming loop for a given
  671.  *    relation works, the tids of vacuumed tuples wind up in reverse
  672.  *    order in the list -- highest tid on a page is first, and higher
  673.  *    pages come before lower pages.  This is important later when we
  674.  *    vacuum the indices, as it gives us a way of stopping the search
  675.  *    for a tid if we notice we've passed the page it would be on.
  676.  */
  677.  
  678. void
  679. _vc_reaptid(p, curvrl, blkno, offind)
  680.     Portal p;
  681.     VRelList curvrl;
  682.     BlockNumber blkno;
  683.     OffsetIndex offind;
  684. {
  685.     PortalVariableMemory pmem;
  686.     MemoryContext old;
  687.     VTidList newvtl;
  688.  
  689.     /* allocate a VTidListData entry in the portal memory context */
  690.     pmem = PortalGetVariableMemory(p);
  691.     old = MemoryContextSwitchTo((MemoryContext) pmem);
  692.     newvtl = (VTidList) palloc(sizeof(VTidListData));
  693.     MemoryContextSwitchTo(old);
  694.  
  695.     /* fill it in */
  696.     ItemPointerSet(&(newvtl->vtl_tid), 0, blkno, 0, offind + 1);
  697.     newvtl->vtl_next = curvrl->vrl_tidlist;
  698.     curvrl->vrl_tidlist = newvtl;
  699. }
  700.  
  701. void
  702. _vc_free(p, vrl)
  703.     Portal p;
  704.     VRelList vrl;
  705. {
  706.     VRelList p_vrl;
  707.     VAttList p_val, val;
  708.     VTidList p_vtl, vtl;
  709.     MemoryContext old;
  710.     PortalVariableMemory pmem;
  711.  
  712.     pmem = PortalGetVariableMemory(p);
  713.     old = MemoryContextSwitchTo((MemoryContext)pmem);
  714.  
  715.     while (vrl != (VRelList) NULL) {
  716.  
  717.     /* free attribute list */
  718.     val = vrl->vrl_attlist;
  719.     while (val != (VAttList) NULL) {
  720.         p_val = val;
  721.         val = val->val_next;
  722.         pfree (p_val);
  723.     }
  724.  
  725.     /* free tid list */
  726.     vtl = vrl->vrl_tidlist;
  727.     while (vtl != (VTidList) NULL) {
  728.         p_vtl = vtl;
  729.         vtl = vtl->vtl_next;
  730.         pfree (p_vtl);
  731.     }
  732.  
  733.     /* free rel list entry */
  734.     p_vrl = vrl;
  735.     vrl = vrl->vrl_next;
  736.     pfree (p_vrl);
  737.     }
  738.  
  739.     (void) MemoryContextSwitchTo(old);
  740. }
  741.  
  742. /*
  743.  *  _vc_getarchrel() -- open the archive relation for a heap relation
  744.  *
  745.  *    The archive relation is named 'a,XXXXX' for the heap relation
  746.  *    whose relid is XXXXX.
  747.  */
  748.  
  749. #define ARCHIVE_PREFIX    "a,"
  750.  
  751. Relation
  752. _vc_getarchrel(heaprel)
  753.     Relation heaprel;
  754. {
  755.     Relation archrel;
  756.     Name archrelname;
  757.  
  758.     archrelname = (Name) palloc(sizeof(NameData));
  759.     sprintf(&(archrelname->data[0]), "%s%ld", ARCHIVE_PREFIX, heaprel->rd_id);
  760.  
  761.     archrel = heap_openr(archrelname);
  762.  
  763.     return (archrel);
  764. }
  765.  
  766. /*
  767.  *  _vc_archive() -- write a tuple to an archive relation
  768.  *
  769.  *    In the future, this will invoke the archived accessd method.  For
  770.  *    now, archive relations are on mag disk.
  771.  */
  772.  
  773. void
  774. _vc_archive(archrel, htup)
  775.     Relation archrel;
  776.     HeapTuple htup;
  777. {
  778.     double ignore;
  779.  
  780.     doinsert(archrel, htup);
  781. }
  782.  
  783. bool
  784. _vc_isarchrel(rname)
  785.     Name rname;
  786. {
  787.     if (strncmp(ARCHIVE_PREFIX, &(rname->data[0]), strlen(ARCHIVE_PREFIX)) == 0)
  788.     return (true);
  789.  
  790.     return (false);
  791. }
  792.